# Sap-Win.tcl Simulation for ArduPilot GUI functionality
#
# Copyright (C) 2010  Paul Hopkins  email:- hopslink@gmail.com
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

package require Tk

# sap win
# Specifies all GUI widgets and their layout
# Also sets associated event handlers for widgets
#
proc ::sap::win {} {		;#{win .send_file}
	variable S
	
	# Open a window
	wm title . "GSlite - DIY Drones"
        
	#MENU
	
	# Draw the menu
	option add *tearOff 0;  # prevent tear off menu behaviour
        
	menu .menubar
        . configure -menu .menubar
        set mb .menubar
        
        menu $mb.file
        #menu $mb.serial
	menu $mb.help
	menu $mb.file.export
        $mb add cascade -menu $mb.file -label " File "  
        #$mb add cascade -menu $mb.serial -label Serial
	$mb add cascade -menu $mb.help -label " Help " 
        
        $mb.file add command -label "Open..." -command "::sap::loadFile"
        $mb.file add command -label "Save" -command "::sap::saveFile"
	$mb.file add separator
	$mb.file add cascade -menu $mb.file.export -label "Export" 
	$mb.file.export add command -label ".kml" -command "::sap::exportKml"
	$mb.file.export add command -label ".csv" -command "::sap::exportCsv"
	
	$mb.help add command -label "Help" -command "::sap::showHelp"
	$mb.help add command -label "About" -command "::sap::showAbout"
	
	# WIDGETS
	
	# Add a main frame to hold all our widgets
	set mf [ttk::frame .frame]
	.frame configure -padding 5
	
	# Labels to variables
	::ttk::label $mf.spatialLabel -text {Telemetry:}
	::ttk::label $mf.waypointLabel -text {Waypoint:}
	
	::ttk::label $mf.latLabel -text {Latitude:} -anchor e
	::ttk::label $mf.lonLabel -text {Longitude:} -anchor e
	::ttk::label $mf.spdLabel -text {Speed:} -anchor e
	::ttk::label $mf.crtLabel -text {Climb Rate:} -anchor e
	::ttk::label $mf.altLabel -text {Altitude:} -anchor e
	::ttk::label $mf.crsLabel -text {Heading:} -anchor e
	::ttk::label $mf.berLabel -text {Bearing:} -anchor e
	::ttk::label $mf.wpnLabel -text {Number:} -anchor e
	::ttk::label $mf.dstLabel -text {Distance:} -anchor e
	::ttk::label $mf.btvLabel -text {Batt. Voltage:} -anchor e
	::ttk::label $mf.rspLabel -text {Servo Roll:} -anchor e
	::ttk::label $mf.towLabel -text {iTOW:} -anchor e
	
	# variable values
	::ttk::label $mf.latDataLabel -width 12 -textvariable ::sap::S(latData) 
	::ttk::label $mf.lonDataLabel -textvariable ::sap::S(lonData) 
	::ttk::label $mf.spdDataLabel -textvariable ::sap::S(spdData) 
	::ttk::label $mf.crtDataLabel -textvariable ::sap::S(crtData)
	::ttk::label $mf.altDataLabel -textvariable ::sap::S(altData)
	::ttk::label $mf.crsDataLabel -textvariable ::sap::S(crsData) 
	::ttk::label $mf.berDataLabel -textvariable ::sap::S(berData)
	::ttk::label $mf.wpnDataLabel -textvariable ::sap::S(wpnData)
	::ttk::label $mf.dstDataLabel -textvariable ::sap::S(dstData)
	::ttk::label $mf.btvDataLabel -textvariable ::sap::S(btvData) 
	::ttk::label $mf.rspDataLabel -textvariable ::sap::S(rsptData)
	::ttk::label $mf.towDataLabel -textvariable ::sap::S(towData)
	
	# separators
	::ttk::separator $mf.midVerticalSeparator -orient vertical
	::ttk::separator $mf.leftHoriz_Separator1 -orient horizontal
	::ttk::separator $mf.leftHoriz_Separator2 -orient horizontal
	::ttk::separator $mf.rightHoriz_Separator1 -orient horizontal
	
	#widgets for servo positions...
	# labels
	::ttk::label $mf.controlLabel -text {Controls:}
	::ttk::label $mf.throttleLabel -text {Throttle:-}
	::ttk::label $mf.elevatorLabel -text {Elevator:-}
	::ttk::label $mf.aileronLabel -text {Ailerons:-}    ;#TODO Allow user specified control types
	# scale widgets for servo data feedback 
	::ttk::scale $mf.throttleScale -variable ::sap::S(throttleData) -from 0.0 -to 1.0 
	::ttk::scale $mf.elevatorScale -variable ::sap::S(elevatorData) -from -1.0 -to 1.0
	::ttk::scale $mf.aileronScale -variable ::sap::S(aileronData) -from -1.0 -to 1.0
	
	#widgets for comms
	#labels
	::ttk::label $mf.simLabel -text {Simulator:}
	::ttk::label $mf.commsfgHostLabel -text {Host:} -anchor e
	::ttk::label $mf.commsfgPortOutLabel -text {Output Port:} -anchor e
	::ttk::label $mf.commsfgPortInLabel -text {Input Port:} -anchor e
	#separator
	::ttk::label $mf.serialLabel -text {ArduPilot:}
	::ttk::label $mf.commsSerialLabel -text {Serial Port:} -anchor e
	::ttk::label $mf.commsBaudLabel -text {Baud Rate:} -anchor e
	
	#tcp/udp selectors
	# Simulator host address
	ttk::entry $mf.fgHostSelector -textvariable ::sap::S(comm.fgHost) -width 10
		
	ttk::entry $mf.fgPortSelector -textvariable ::sap::S(comm.fgPort) -width 6
		
	ttk::entry $mf.fgServiceSelector -textvariable ::sap::S(comm.fgServicePort) -width 6
	
	
	#serial port selector 
	::ttk::combobox $mf.serialPortSelector -textvariable ::sap::S(comm.SerialPort) -width 6
	$mf.serialPortSelector configure -values [list COM1: COM2: COM3: COM4: COM5: COM6: COM7: COM8: COM9:]
	
	::ttk::combobox $mf.serialBaudSelector -textvariable ::sap::S(comm.SerialBaudRate) -width 6
	$mf.serialBaudSelector configure -values [list 9600 19200 38400 57600 115200]
	
	
	# Buttons
	ttk::button $mf.commConnect -text "Connect" -command "::sap::commConnect"
	set S(win.commConnectButton) $mf.commConnect
	ttk::button $mf.commDisconnect -text "Disconnect" -command "::sap::commDisconnect"
	set S(win.commDisconnectButton) $mf.commDisconnect
	$S(win.commDisconnectButton) state disabled
	
	# LAYOUT
	
	# layout widgets
	# frame in main window
	grid .frame -column 0 -row 0 -sticky nwes
	grid columnconfigure . 0 -weight 1
	grid rowconfigure . 0 -weight 1
	
	grid columnconfigure .frame [list 3 4 6 7] -weight 1
	grid rowconfigure .frame all -weight 1
		
	#widgets in frame
	grid $mf.spatialLabel -column 3 -row 0 -sticky nsew 
	grid $mf.waypointLabel -column 3 -row 8 -sticky nsew 
	
	grid $mf.latLabel -column 3 -row 1 -sticky nsew 
	grid $mf.lonLabel -column 3 -row 2 -sticky nsew 
	grid $mf.altLabel -column 3 -row 3 -sticky nsew 
	grid $mf.spdLabel -column 3 -row 4 -sticky nsew 
	grid $mf.crtLabel -column 3 -row 5 -sticky nsew 
	grid $mf.crsLabel -column 3 -row 6 -sticky nsew 
	# separator
	# waypoint label 
	grid $mf.berLabel -column 3 -row 9 -sticky nsew 
	grid $mf.wpnLabel -column 3 -row 11 -sticky nsew 
	grid $mf.dstLabel -column 3 -row 10 -sticky nsew 
	#separator
	grid $mf.btvLabel -column 3 -row 13 -sticky nsew 
	grid $mf.rspLabel -column 3 -row 14 -sticky nsew 
	grid $mf.towLabel -column 3 -row 15 -sticky nsew 
	
	grid $mf.latDataLabel -column 4 -row 1 -sticky nsew 
	grid $mf.lonDataLabel -column 4 -row 2 -sticky nsew 
	grid $mf.altDataLabel -column 4 -row 3 -sticky nsew 
	grid $mf.spdDataLabel -column 4 -row 4 -sticky nsew 
	grid $mf.crtDataLabel -column 4 -row 5 -sticky nsew 
	grid $mf.crsDataLabel -column 4 -row 6 -sticky nsew 
	# separator
	# waypoint label 	
	grid $mf.berDataLabel -column 4 -row 9 -sticky nsew 
	grid $mf.wpnDataLabel -column 4 -row 11 -sticky nsew 
	grid $mf.dstDataLabel -column 4 -row 10 -sticky nsew 
	#separator
	grid $mf.btvDataLabel -column 4 -row 13 -sticky nsew 
	grid $mf.rspDataLabel -column 4 -row 14 -sticky nsew 
	grid $mf.towDataLabel -column 4 -row 15 -sticky nsew 
	
	# servo widget layout 
	grid $mf.aileronScale -column 6 -row 2 -columnspan 2 -sticky nsew 
	grid $mf.elevatorScale -column 6 -row 4 -columnspan 2 -sticky nsew 
	grid $mf.throttleScale -column 6 -row 6 -columnspan 2 -sticky nsew 
	
	grid $mf.controlLabel -column 6 -row 0 -sticky nsew 
	grid $mf.aileronLabel -column 6 -row 1 -sticky nsew 
	grid $mf.elevatorLabel -column 6 -row 3 -sticky nsew 
	grid $mf.throttleLabel -column 6 -row 5 -sticky nsew 
	
	#separator layout
	grid $mf.midVerticalSeparator -column 5 -row 0 -rowspan 18 -padx 5 -sticky nsew 
	grid $mf.leftHoriz_Separator1 -column 3 -row 7 -columnspan 2 -sticky nsew 
	grid $mf.leftHoriz_Separator2 -column 3 -row 12 -columnspan 2 -sticky nsew 
	grid $mf.rightHoriz_Separator1 -column 6 -row 7 -columnspan 2 -sticky nsew 
	
	# comms widget layout
	#labels	
	grid $mf.simLabel -column 6 -row 8 -sticky nsew 
	grid $mf.commsfgHostLabel -column 6 -row 9 -sticky nsew 
	grid $mf.commsfgPortOutLabel -column 6 -row 10 -sticky nsew 
	grid $mf.commsfgPortInLabel -column 6 -row 11 -sticky nsew 
	
	grid $mf.serialLabel -column 6 -row 13 -sticky nsew 
	grid $mf.commsSerialLabel -column 6 -row 14 -sticky nsew
	grid $mf.commsBaudLabel -column 6 -row 15 -sticky nsew
	
	# selectors	
	grid $mf.fgHostSelector -column 7 -row 9 -sticky nsew 
	grid $mf.fgPortSelector -column 7 -row 10 -sticky nsew 
	grid $mf.fgServiceSelector -column 7 -row 11 -sticky nsew 
	
	grid $mf.serialPortSelector -column 7 -row 14 -sticky nsew 
	grid $mf.serialBaudSelector -column 7 -row 15 -sticky nsew
	# buttons
	grid $mf.commConnect -column 6 -row 17 -sticky nsew 
	grid $mf.commDisconnect -column 7 -row 17 -sticky nsew 
	
	# show window
	wm deiconify .		
}


# showHelp
# shows popup messagebox with pointers on obtaining help with SAP
#
proc ::sap::showHelp {} {
	set helpMessage "For help see the included SAP_README.html document. \n \
		\n \
		Still stuck? ask on the DIY Drones forum: http://www.diydrones.com"
	
	set answer [tk_messageBox -title "Help" -message "$helpMessage" -type ok ]
}


# showAbout
# shows popup messagebox with details about SAP
#
proc ::sap::showAbout {} {
	variable S
	# 
	# Insert your name 
	set aboutMessage "SAP - Simulation for ArduPilot \n \
		version $S(sap_version) \n \
		\n \
		By: \n \
	 	\thopslink \n \
		\t... \n \
	 	for DIYDrones"
		
	set detailMessage "SAP Copyright (C) 2010  Paul Hopkins \n \
		This program comes with ABSOLUTELY NO WARRANTY. \n \
		This is free software, and you are welcome to \n \
		redistribute it under certain conditions... \n \
		See http://www.gnu.org/licenses/ \n \
		or included license.txt for details"	
	
	set answer [tk_messageBox -title "About SAP" -message "$aboutMessage" -detail $detailMessage -type ok ]
}


# displayInData
# displays telemetry data from ArduPilot on screen.
# GUI widgets have associated variables (see ::sap::win)
# To update the display simply update these variables.
#
# Arguments:
#  InDataString - telemetry packet from ArduPilot
#
proc ::sap::win_displayInData {inDataString} {
	variable S
	# convert input data to list
	set S(lastInData) [split $inDataString ",:"]
	
	# correct units where neccessary and display
	# latitude
	set tmpLatData [lindex $S(lastInData) 1]
	set S(latData) [expr {$tmpLatData / $S(scaleAPLocationInt2Float)}]
	# longitude
	set tmpLonData [lindex $S(lastInData) 3]	
	set S(lonData) [expr {$tmpLonData / $S(scaleAPLocationInt2Float)}]
	
	set S(spdData) [lindex $S(lastInData) 5]
	set S(crtData) [lindex $S(lastInData) 7]
	set S(altData) [lindex $S(lastInData) 9]
	set S(alhData) [lindex $S(lastInData) 11]
	
	# heading
	set tmpCrsData	[lindex $S(lastInData) 13]
	# convert -180 to 180 > 0 to 360 degrees 
	if {$tmpCrsData < 0} {		
		set tmpCrsData [expr {$tmpCrsData + 360}]
	}
	set S(crsData) $tmpCrsData
	
	set S(berData) [lindex $S(lastInData) 15]
	set S(wpnData) [lindex $S(lastInData) 17]
	set S(dstData) [lindex $S(lastInData) 19]
	set S(btvData) [lindex $S(lastInData) 21]
	set S(rspData) [lindex $S(lastInData) 23]
	set S(towData) [lindex $S(lastInData) 25]	
}


# displayControls - update control positions
#
# GUI widgets have associated variables (see ::sap::win)
# To update the display simply update these variables.
#
# Arguments:
#  controlData - tcl list with normalized servo delay values 
#
proc ::sap::win_displayControls {controlData} {
	set ::sap::S(throttleData) [lindex $controlData end]
	set ::sap::S(elevatorData) [lindex $controlData 1]
	set ::sap::S(aileronData) [lindex $controlData 0]	
}				


# StatusConnected - update GUI for comms connection status
#
proc ::sap::win_StatusConnected {} {
	variable S
	# update the status of the connection buttons
	$S(win.commConnectButton) state disabled
	$S(win.commDisconnectButton) state !disabled	
}


# win_StatusConnected
# updates display on comm port connection
# Note - currently this is a 1-shot process since flightgear closes on disconnect?
# So we disconnect both buttons to prevent further comms attempts and force a restart
#
proc ::sap::win_StatusDisconnected {} {
	variable S
	# update the status of the connection buttons
	$S(win.commConnectButton) state disabled
	$S(win.commDisconnectButton) state disabled	
}
